package com.fyl.verify.utils;

import com.fyl.verify.anno.ParamVerifyRule;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;

public class ParamVerifyUtils {

    public static List<String> deepVerify(Object param) throws Exception {
        ArrayList<String> messages = new ArrayList<>();
        if (!needToCheck(param)) {
            return messages;
        }
        if (param instanceof Collection<?>) {
            Collection<?> params = (Collection<?>) param;
            if (!params.isEmpty()) {
                for (Object element : params) {
                    messages.addAll(deepVerify(element));
                }
            }
            return messages;
        }

        Class<?> clazz = param.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            Object value = field.get(param);
            ParamVerifyRule[] rules = field.getAnnotationsByType(ParamVerifyRule.class);
            String message = doVerify(param, value, rules);
            if (StringUtils.isNotBlank(message)) {
                messages.add(message);
            }
            // value 递归检查
            if (needToCheck(value)) {
                messages.addAll(deepVerify(value));
            }
        }
        return messages;
    }


    public static String verify(Object param) throws Exception {
        String message = "";
        if (!needToCheck(param)) {
            return message;
        }
        if (param instanceof Collection<?>) {
            Collection<?> params = (Collection<?>) param;
            if (!params.isEmpty()) {
                for (Object element : params) {
                    message = verify(element);
                    if (StringUtils.isNotBlank(message)) {
                        return message;
                    }
                }
            }
            return message;
        }

        Class<?> clazz = param.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            Object value = field.get(param);
            ParamVerifyRule[] rules = field.getAnnotationsByType(ParamVerifyRule.class);
            message = doVerify(param, value, rules);
            if (StringUtils.isNotBlank(message)) {
                return message;
            }
            // value 递归检查
            if (needToCheck(value)) {
                message = verify(value);
                if (StringUtils.isNotBlank(message)) {
                    return message;
                }
            }
        }
        return message;
    }


    private static String doVerify(Object param, Object value, ParamVerifyRule... rules) throws Exception {
        for (ParamVerifyRule anno : rules) {
            if (anno.required() && isEmpty(value)) {
                return anno.message();
            }
            if (StringUtils.isNotBlank(anno.patten()) && !isEmpty(value) &&
                    !Pattern.matches(anno.patten(), String.valueOf(value))) {
                return anno.message();
            }
            String[] methods = anno.dycMethods();
            for (String method : methods) {
                if (!executeMethod(method, value, param)) {
                    return anno.message();
                }
            }
        }
        return "";
    }

    /**
     * 判断类型是否是需要检查
     * param 三种情况不需要检查
     * 1. 为空
     * 2. 是 MultipartFile 类型
     * 3. 是 java 原生类型(不是Collection的)
     *
     * @param param 参数
     * @return 是否需要检查
     */
    private static boolean needToCheck(Object param) {
        return null != param &&
                !(param instanceof MultipartFile || param instanceof MultipartFile[]) &&
                !(param instanceof HttpServletRequest || param instanceof HttpServletResponse) &&
                (!isJavaClass(param.getClass()) || param instanceof Collection<?>);
    }

    private static boolean isJavaClass(Class<?> clz) {
        return clz != null && clz.getClassLoader() == null;
    }

    private static boolean executeMethod(String method, Object field, Object arg) throws Exception {
        if (StringUtils.isBlank(method) || field == null) {
            return true;
        }
        String[] strings = method.split(".");
        String className = strings[0];
        String methodName = strings[1];

        Class<?> cls = Class.forName(className);
        Object bean;
        try {
            bean = SpringContextUtils.getBean(cls);
        } catch (Exception e) {
            bean = cls.newInstance();
        }

        try {
            Method m2 = cls.getDeclaredMethod(methodName, field.getClass());
            return (boolean) m2.invoke(bean, field);
        } catch (NoSuchMethodException e) {
            Method m3 = cls.getDeclaredMethod(methodName, field.getClass(), arg.getClass());
            return (boolean) m3.invoke(bean, field, arg);
        }
    }

    private static boolean isEmpty(Object o) {
        if (o instanceof String) {
            return StringUtils.isBlank((String) o);
        } else if (o instanceof Collection) {
            Collection<?> collection = (Collection<?>) o;
            return collection.isEmpty();
        }
        return null == o;
    }

}
